#ifndef __MATH__H
#define __MATH__H
#include <cmath>
#include <cstdlib>
#include "types.h"

class Math
{
public:
	static void Init() {
		for(U32 i = 0; i < 360; i++) m_nAngleSinLut[i] = ::sin((F64)i * M_PI / 180.0);
		for(U32 j = 0; j < 4096; j++) m_nFloorSqrtLut[j] = (U32)::sqrt((S32)j); 
		for(U32 k = 0; k <= 4096; k++) {
			S32 nValue = (S32)(::atan(k / 4096.0) * 180.0 / M_PI);
			if (nValue < 0 || nValue >= 360) abort();
			m_nAngleOfPointsLut[k] = nValue;
		}
	}
	static F64 Sin(F64 nDegree) {
		nDegree = NormalizeAngle(nDegree);
		F64 diff = nDegree - (S32)nDegree;
		return m_nAngleSinLut[(S32)nDegree] * (1 - diff) + m_nAngleSinLut[(S32)nDegree + 1] * diff;
	}
	static F64 Cos(F64 nDegree) { return Math::Sin(90 - nDegree); }  
	static F64 AngleDiff(F64 nDif) {
		while(nDif < 0) nDif += 360.0;
		while(nDif >= 360) nDif -= 360.0;
		return nDif > 180 ? nDif - 360 : nDif;		
	}
	static F64 AngleDist(F64 nDif) { return fabs(AngleDiff(nDif)); }
	static U32 AngleOfPoints(F64 nDistHorz, F64 nDistVert) {
		bool bSwapped = fabs(nDistHorz) < fabs(nDistVert);
		S32 nRelation;
		if (bSwapped)
			nRelation = (S32)(nDistHorz * 4096 / nDistVert);
		else
			nRelation = (S32)(nDistVert * 4096 / nDistHorz);
		bool bReversed = nRelation < 0;
		if (bReversed)
			nRelation = -nRelation;
		U32 nResult = m_nAngleOfPointsLut[nRelation]; // atan(nDist1 / nDist2);
		U32 nRet;
		if (!bSwapped)
			if (!bReversed)
				if (nDistHorz > 0)
					nRet = nResult;
				else
					nRet = nResult + 180; 
			else
				if (nDistHorz > 0)
					nRet = nResult ? 360 - nResult : 0;
				else
					nRet = 180 - nResult;
		else
			if (!bReversed)
				if (nDistVert > 0)
					nRet = 90 - nResult;
				else
					nRet = 270 - nResult;
			else
				if (nDistVert > 0)
					nRet = nResult + 90;
				else
					nRet = nResult + 270;
		
		if (nRet >= 360) abort();
		return nRet;
	}
/*	static F64 sin(F64 nDegree) { nDegree = Normalize(nDegree); return ::sin(nDegree * M_PI / 180.0); }*/
	static F64 NormalizeAngle(F64 degree)
	{
		degree = ((S32)degree % 360) + (degree - (S32)degree);
		if (degree < 0) degree = 360 + degree;
		return degree;
	}
	static F64 PointDist2(double nRowDiff, double nColDiff)
	{
		return nRowDiff * nRowDiff + nColDiff * nColDiff;
	}
	static U32 FloorSqrt(U32 value)
	{
		if (value >= 4096)
			abort();
		return m_nFloorSqrtLut[value];
	}
	template <class T> static T Sqr(T value) { return value * value; } 
private:
	static F64 m_nAngleSinLut[360];
	static U32 m_nAngleOfPointsLut[4097];
	static U32 m_nFloorSqrtLut[4096];
	
};
#endif
